﻿#include "businessinit.h"

#include "inputhandler.h"
#include "AppConfig/appconfig.h"
#include <filesystem>
#include <fstream>
#include <functional>
#include "businessmsghandler.h"

#include <QFile>
#include <QDataStream>
#include <QIODevice>
#include <QJsonDocument>
#include <QJsonParseError>

#include "CipherUtils/cipherutils.h"

#include "SyncManager/syncmanager.h"

#include "businessnetwork.h"
#include "../Network/network.h"
#include "businesschat.h"
#include "businessfile.h"
#include <FileManager/filemanager.h>
#include <ThreadPool/threadpool.h>
#include "businessuser.h"
#include "businessgroup.h"
#include "businesssql.h"
#include <Debug/DEBUG.hpp>

BusinessInit &BusinessInit::getInstance()
{
    static BusinessInit instance;
    return instance;
}

bool BusinessInit::InitModules()
{
    stop = false;
    // 判断当前文件夹下是不是有全局配置文件，如果没有，则创建一个
    std::string configFilePath = "./CONFIG";
    if (!std::filesystem::exists(configFilePath)) {
        std::ofstream ofs(configFilePath);
        if (!ofs) {
            qWarning() << "创建全局配置文件失败：" << configFilePath;
            return false;
        }
        ofs.close();
    }

    BusinessNetWork::getInstance().initNet();

    AppConfig::getInstance().setRuntimeValue("Authenticated", "false");
    AppConfig::getInstance().setRuntimeValue("ConnectionStatus", "false");
    AppConfig::getInstance().setRuntimeValue("InitStatus", "false");

    BusinessMsgHandler::getInstance().init();

    // 初始回调函数
    InputHandler& inputhandler = InputHandler::getInstance();
    inputhandler.registerCallback("DialogIniProgram::on_btnOK_clicked",
                                  std::bind(&BusinessInit::readLocalConfigure,
                                            &BusinessInit::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    // 生成一个密钥文件
    inputhandler.registerCallback("DialogIniProgram::on_btnRegenerate_clicked",
                                  std::bind(&BusinessInit::generateAESKeyFile,
                                            &BusinessInit::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    // 登陆服务器
    inputhandler.registerCallback("DialogLogin::on_buttonBox_accepted",
                                  std::bind(&BusinessInit::connectAndLoginTargetServer,
                                            &BusinessInit::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    // send message to friend
    inputhandler.registerCallback("ChatWindow::on_BtnSend_clicked",
                                  std::bind(&BusinessChat::chatEvent,
                                            &BusinessChat::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));

    // request offline message of a friend
    inputhandler.registerCallback("MainWindow::newChatWindow",
                                  std::bind(&BusinessChat::requestOfflineMessage,
                                            &BusinessChat::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));

    // upload file
    inputhandler.registerCallback("MainWindow::on_btnUploadFile_clicked",
                                  std::bind(&BusinessFile::uploadFile,
                                            &BusinessFile::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    // download a file
    inputhandler.registerCallback("MainWindow::on_btnDownloadFile_clicked",
                                  std::bind(&BusinessFile::downloadFile,
                                            &BusinessFile::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    // refresh file list
    inputhandler.registerCallback("MainWindow::on_btnRefreshFileList_clicked",
                                  std::bind(&BusinessFile::refreshFileList,
                                            &BusinessFile::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));

    inputhandler.registerCallback("FriendRequestList::buttonClicked",
                                  std::bind(&BusinessUser::replyFriendApply,
                                            &BusinessUser::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("MainWindow::on_btnAddFriend_clicked",
                                  std::bind(&BusinessUser::applyAddFriend,
                                            &BusinessUser::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("MainWindow::on_btnRefreshUserList_clicked",
                                  std::bind(&BusinessUser::applyForRefreshUserList,
                                            &BusinessUser::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("MainWindow::closeEvent",
                                  std::bind(&BusinessInit::closeProgram,
                                            &BusinessInit::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("MainWindow::do_eraseFriend",
                                  std::bind(&BusinessUser::eraseFriend,
                                            &BusinessUser::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("MainWindow::on_btnCreateGroup_clicked",
                                  std::bind(&BusinessGroup::createGroup,
                                            &BusinessGroup::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));

    inputhandler.registerCallback("MainWindow::on_btnRefreshGroupList_clicked",
                                  std::bind(&BusinessGroup::applyForRefreshGroupList,
                                            &BusinessGroup::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("MainWindow::on_btnJoinGroup_clicked",
                                  std::bind(&BusinessGroup::applyForJoinAGroup,
                                            &BusinessGroup::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("MainWindow::on_btnQuitGroup_clicked",
                                  std::bind(&BusinessGroup::leaveGroup,
                                            &BusinessGroup::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("GroupRequestList::buttonClicked",
                                  std::bind(&BusinessGroup::replyGroupApply,
                                            &BusinessGroup::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("GroupRequestList::on_btnRefreshRequestList_clicked",
                                  std::bind(&BusinessGroup::applyForRefreshGroupRequestList,
                                            &BusinessGroup::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("ChatWindow::getUserHistoryMessage",
                                  std::bind(&BusinessUser::updateLocalMessage,
                                            &BusinessUser::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));

    inputhandler.registerCallback("ChatWindow::getGroupHistoryMessage",
                                  std::bind(&BusinessGroup::updateLocalMessage,
                                           &BusinessGroup::getInstance(),
                                           std::placeholders::_1,
                                           std::placeholders::_2));
    inputhandler.registerCallback("AvatarSelector::on_btnAccept_clicked",
                                  std::bind(&BusinessUser::updateAvatar,
                                            &BusinessUser::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("ChatWindow::on_BtnSendPic_clicked",
                                  std::bind(&BusinessChat::sendPicture,
                                            &BusinessChat::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    inputhandler.registerCallback("ChatWindow::newChat",
                                  std::bind(&BusinessChat::getPicture,
                                            &BusinessChat::getInstance(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));

    FileManager& fileManager = FileManager::getInstance();
    fileManager.init();
    // init file manager
    FileManager::getInstance().setAutoWriteEncrypted(false);
    ThreadPool::getInstance().commit([&](){fileManager.processQueue();});
    // BusinessSQL::getInstance().init();
    return true;
}

void BusinessInit::saveGlobalConfig()
{
    std::ofstream ofs("./CONFIG");
    if (!ofs) {
        qDebug() << "文件保存失败！";
        return ;
    }


    AppConfig& config = AppConfig::getInstance();
    if (config.runtimeValue("Authenticated") == "false") {
        return;
    }
    std::string AESKey = config.runtimeValue("LocalAESKey");
    std::string IV = config.runtimeValue("LocalAESIV");

    nlohmann::json globalJson = config.getGlobalStore();
    std::string globalJsonStr = globalJson.dump();

    CipherUtils& utils = CipherUtils::getInstance();

    auto globalJsonAES = utils.AES_Encrypt(globalJsonStr.c_str(),
                                           globalJsonStr.size(),
                                           (unsigned char*)AESKey.c_str(),
                                           (unsigned char*)IV.c_str());

    auto globalJsonBase64 = utils.Base64_Encode(globalJsonAES.first.get(), globalJsonAES.second);

    ofs<<globalJsonBase64;
    ofs.close();

}

void BusinessInit::closeProgram(QJsonObject &in, QJsonObject &out)
{
    stop = true;
    // 保存设置
    saveGlobalConfig();
    // 核心模块停止
    SyncManager::getInstance().stop();
    // 基础模块停止
    Network& net = Network::getInstance();
    net.join();
    FileManager::getInstance().stop();
    ThreadPool::getInstance().stop();
}

BusinessInit::BusinessInit() {}

bool BusinessInit::isStop()
{
    return stop;
}

void BusinessInit::readLocalConfigure(QJsonObject& in, QJsonObject &out)
{
    QString filePath = in["AES256FilePath"].toString();
    qDebug() << "business 已读取路径：" + filePath;
    out.insert("status", "true");
    // 读取AES的base64文件
    std::ifstream ifs(filePath.toStdString(), std::ios::in);
    if (!ifs) {
        qWarning() << "打开文件失败: " + filePath.toStdString();
        out["status"] = false;
        return ;
    }

    // 将AES的base64文件全都读到AES_IV_Base64中
    std::string AES_IV_Base64;
    ifs >> AES_IV_Base64;
    ifs.close();
    // 读完文件以后关闭文件


    // 将base64转成二进制AES与IV
    CipherUtils& utils = CipherUtils::getInstance();

    auto res = utils.Base64_Decode(AES_IV_Base64);
    std::shared_ptr<unsigned char[]> AES_IV_Bin = res.first;
    // 切割文件来得到aes密钥与iv
    std::string AESKey;
    std::string IV;
    AESKey.resize(256/8);
    IV.resize(128/8);

    memcpy(AESKey.data(), AES_IV_Bin.get(), 256/8);
    memcpy(IV.data(), AES_IV_Bin.get() + 256/8, 128/8);

    // 读全局配置文件
    ifs.open("./CONFIG", std::ios::in);
    if (!ifs) {
        qWarning() << "打开全局配置文件失败: ./CONFIG";
        out["status"] = false;
        return ;
    }
    std::string configFileBase64;
    ifs >> configFileBase64;
    ifs.close();
    // 如果当前的文件是空的，则说明是刚创立
    if (configFileBase64.empty()) {
        AppConfig::getInstance().setRuntimeValue("LocalAESKey", AESKey);
        AppConfig::getInstance().setRuntimeValue("LocalAESIV", IV);
        InitConfigFile();
    } else {
        try {
            // 使用密钥去解密文件
            auto configFileBin = utils.Base64_Decode(configFileBase64);
            auto configFileStr = utils.AES_Decrypt(configFileBin.first.get(),
                                                   configFileBin.second,
                                                   (unsigned char*)AESKey.data(),
                                                   (unsigned char*)IV.data());
            std::string configJson = (char *)configFileStr.first.get();

            QJsonParseError error;
            QJsonDocument doc = QJsonDocument::fromJson(configJson.c_str(), &error);
            if (error.error == QJsonParseError::NoError) {
                // 解析成功，说明AES与iv均正确，将其添加到设置中
                AppConfig::getInstance().setRuntimeValue("LocalAESKey", AESKey);
                AppConfig::getInstance().setRuntimeValue("LocalAESIV", IV);
                AppConfig::getInstance().init(configJson);
            } else {
                qDebug() << "解析出错，请判断AES是否正确";
                out["status"] = false;
                return ;
            }
        } catch(std::exception & e) {
            qDebug() << e.what();
            out["status"] = false;
            return ;
        }
    }

}

void BusinessInit::generateAESKeyFile(QJsonObject &in, QJsonObject &out)
{
    QString QfilePath = in["targetPath"].toString();
    qDebug() << "business 已读取路径：" + QfilePath;
    out.insert("status", "true");
    // 读取AES的base64文件
    std::string filePath(QfilePath.toStdString() + (std::string)"/AES.key");
    qDebug() << "目标路径" + filePath;
    std::ofstream ofs (filePath);

    if (!ofs) {
        qDebug() << "目标路径打开失败：" + filePath;
        out["status"] = false;
        return ;
    }

    CipherUtils& utils = CipherUtils::getInstance();

    auto AES_IV = utils.AES_generate();

    std::shared_ptr<unsigned char[]> AES_IV_BIN(new unsigned char[256/8 + 128/8]);

    memcpy(AES_IV_BIN.get(), AES_IV[0].get(), 256/8);
    memcpy(AES_IV_BIN.get() + 256/8, AES_IV[1].get(), 128/8);

    std::string AES_IV_BASE64 = utils.Base64_Encode(AES_IV_BIN.get(), 256/8 + 128/8);

    ofs<<AES_IV_BASE64;
    out["outputPath"] = QfilePath + "/AES.key";
    ofs.close();
}

void BusinessInit::connectAndLoginTargetServer(QJsonObject &in, QJsonObject &out)
{
    AppConfig& config = AppConfig::getInstance();

    std::string IP = in["IP"].toString().toStdString();
    std::string account = in["account"].toString().toStdString();
    std::string password = in["password"].toString().toStdString();
    ///////////////////////
    std::string DBHost = in["DBHost"].toString().toStdString();
    std::string DBPort = in["DBPort"].toString().toStdString();
    std::string DBUserName = in["DBUserName"].toString().toStdString();
    std::string DBUserPwd = in["DBUserPwd"].toString().toStdString();
    ////////////////
    std::string shaPassword = CipherUtils::getInstance().SHA256_calculate(password);

    // connect to the server

    if (!BusinessNetWork::getInstance().connectTargetServer(IP)) {
        out.insert("status", "false");
        return ;
    }

    Network::getInstance().start();

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    bool flag = loginTargetServer(account, shaPassword);
    // wait for server reply
    if (flag) {
        // login success
        out.insert("status", "true");
        config.setRuntimeValue("IP", IP);
        config.setRuntimeValue("Account", account);
        config.setRuntimeValue("Password", shaPassword);
        config.setRuntimeValue("Authenticated", "true");
        // set global IP
        config.setGlobalValue("lastIP", IP);
        config.setGlobalValue("SQLHostName", DBHost);
        config.setGlobalValue("SQLPort", DBPort);
        config.setGlobalValue("SQLUserName", DBUserName);
        config.setGlobalValue("SQLUserPwd", DBUserPwd);

        // init the database
        BusinessSQL::getInstance().init();
        BusinessSQL::getInstance().useDatabase();
    } else {
        // login failed
        out.insert("status", "false");
        config.setRuntimeValue("Authenticated", "false");
    }
}

bool BusinessInit::loginTargetServer(const std::string &account, const std::string &shaPassword)
{
    AppConfig& config = AppConfig::getInstance();
    // set login message
    nlohmann::json messageData;
    messageData["account"] = account;
    messageData["password"] = shaPassword;
    messageData["ECC_public"] = config.globalValue("ECC_public");
    DEBUG_INFO(messageData["ECC_public"]);
    // get the str
    std::string messageToSend = messageData.dump();

    SyncManager& sync = SyncManager::getInstance();
    SyncPoint point = sync.generateUniqueSyncPoint();
    // send the message according to the mode

    BusinessNetWork::getInstance().sendMessageToServer(7, 1, 0, 0, point,
                                                       (unsigned char*)messageToSend.data(),
                                                       messageToSend.size(), true);
    return sync.waitForSyncPoint(point);
}

void BusinessInit::InitConfigFile()
{
    qDebug() << "生成一个全局配置文件";

    std::string jsonStr = R"({"lastIP":"127.0.0.1",
                              "ECC_public":"",
                              "ECC_private":"",
                              "SQLHostName":"127.0.0.1",
                              "SQLPort":"3306",
                              "SQLUserName":"",
                              "SQLUserPwd":""})";

    nlohmann::json json = nlohmann::json::parse(jsonStr);

    CipherUtils& utils = CipherUtils::getInstance();

    auto eccs = utils.ECC_generate();
    std::string ECC_public(std::move(eccs.first));
    std::string ECC_private(std::move(eccs.second));

    json["ECC_public"] = ECC_public;
    json["ECC_private"] = ECC_private;

    AppConfig::getInstance().init(json.dump());

    saveGlobalConfig();
}
